﻿using System;
using System.Workflow.ComponentModel;
using System.Workflow.Runtime.Hosting;
using System.IO;
using Argos.Diagnostics;
using System.Diagnostics;
using System.Workflow.Runtime;

namespace Argos.Hosting
{
	/// <summary>
	/// Represents a workflow persistence service that persists workflow
	/// activities in the file system.
	/// </summary>
	public class FileSystemWorkflowPersistenceService : WorkflowPersistenceService
	{

		public FileSystemWorkflowPersistenceService(string persistenceDirectoryPath)
		{
			this.PersistenceDirectoryPath = persistenceDirectoryPath;
		}


		private string PersistenceDirectoryPath { get; set; }

		private object StreamLock = new object();


		protected override Activity LoadCompletedContextActivity(Guid scopeId, Activity outerActivity)
		{
			Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
			string path = this.GetFileName(instanceId, scopeId);

			lock (this.StreamLock)
			{
				try
				{
					using (FileStream strm = new FileStream(path, FileMode.Open, FileAccess.Read))
					{
						return Activity.Load(strm, outerActivity);
					}
				}
				catch (Exception ex)
				{
					throw new PersistenceException(string.Format("Unable to load activity. InstanceId='{0}'.", instanceId), ex);
				}
			}
		}

		protected override Activity LoadWorkflowInstanceState(Guid instanceId)
		{
			Activity a = null;

			string path = this.GetFileName(instanceId, null);

			lock (this.StreamLock)
			{
				try
				{
					using (FileStream strm = new FileStream(path, FileMode.Open, FileAccess.Read))
					{
						a = Activity.Load(strm, null);
					}
				}
				catch (Exception ex)
				{
					throw new PersistenceException(string.Format("Unable to load workflow instance state for instance '{0}'.", instanceId), ex);
				}
			}

			if (null == a)
			{
				throw new PersistenceException(string.Format("Unable to load state for instance '{0}'.", instanceId));
			}
			return a;
		}

		protected override void SaveCompletedContextActivity(Activity activity)
		{
			Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
			Guid? contextId = null;

			lock (this.StreamLock)
			{
				try
				{
					contextId = (Guid)activity.GetValue(Activity.ActivityContextGuidProperty);
					string path = this.GetFileName(instanceId, contextId);

					using (FileStream strm = new FileStream(path, FileMode.Create, FileAccess.Write))
					{
						activity.Save(strm);
					}
				}
				catch (Exception ex)
				{
					throw new PersistenceException(string.Format("Unable to save activity. InstanceId='{0}'. ContextId='{1}'.", instanceId, contextId), ex);
				}
			}
		}

		protected override void SaveWorkflowInstanceState(Activity rootActivity, bool unlock)
		{
			Guid instanceId = WorkflowEnvironment.WorkflowInstanceId;
			WorkflowStatus status = WorkflowPersistenceService.GetWorkflowStatus(rootActivity);

			try
			{
				switch (status)
				{
					case WorkflowStatus.Completed:
					case WorkflowStatus.Terminated:
						this.DeleteWorkflowInstance(instanceId);
						break;

					default:
						string path = this.GetFileName(instanceId, null);
						lock (this.StreamLock)
						{
							using (FileStream strm = new FileStream(path, FileMode.Create, FileAccess.Write))
							{
								rootActivity.Save(strm);
							}
						}
						break;
				}
			}
			catch (Exception ex)
			{
				throw new PersistenceException(string.Format("Unable to save workflow instance state for instance '{0}'.", instanceId), ex);
			}
		}

		protected override bool UnloadOnIdle(Activity activity)
		{
			return true;
		}

		protected override void UnlockWorkflowInstanceState(Activity rootActivity)
		{
		}



		private string GetFileName(Guid instanceId, Guid? contextId)
		{
			if (contextId.HasValue)
			{
				return string.Format(@"{0}\{1}_{2}.bin", this.PersistenceDirectoryPath, instanceId, contextId.Value);
			}
			return string.Format(@"{0}\{1}.bin", this.PersistenceDirectoryPath, instanceId);
		}

		private void DeleteWorkflowInstance(Guid instanceId)
		{
			lock (this.StreamLock)
			{
				string[] files = Directory.GetFiles(this.PersistenceDirectoryPath, string.Format("{0}*", instanceId));
				foreach (string file in files)
				{
					try
					{
						File.Delete(file);
					}
					catch (Exception ex)
					{
						Tracer.TraceData(TraceEventType.Error, 0, "Unable to delete persisted workflow instance.", Tracer.CreateData("InstanceId", instanceId), Tracer.CreateData("FilePath", file), ex);
					}
				}

			}
		}

	}


}
